class Stack {
  constructor() {
    ;(this.top = 0), // 栈的长度
      (this.list = [])
  }

  push(item) {
    this.top++
    this.list.push(item) // 入栈操作
  }

  pop() {
    --this.top
    return this.list.pop() // 出栈操作
  }

  peek() {
    return this.list[this.top - 1] // 查询栈顶元素
  }
}

/**
 * 对象深拷贝
 */
const deepClone = (data) => {
  const type = getObjType(data)
  let obj
  if (type === 'array') {
    obj = []
  } else if (type === 'object') {
    obj = {}
  } else {
    // 不再具有下一层次
    return data
  }
  if (type === 'array') {
    for (var i = 0, len = data.length; i < len; i++) {
      data[i] = (() => {
        if (data[i] === 0) {
          return data[i]
        }
        return data[i]
      })()
      if (data[i]) {
        delete data[i].$parent
      }
      obj.push(deepClone(data[i]))
    }
  } else if (type === 'object') {
    for (const key in data) {
      if (data) {
        delete data.$parent
      }
      obj[key] = deepClone(data[key])
    }
  }
  return obj
}

// 判断Object类型
const getObjType = (obj) => {
  const { toString } = Object.prototype
  const map = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object',
  }
  if (obj instanceof Element) {
    return 'element'
  }
  return map[toString.call(obj)]
}

/**
 * 树结构数据遍历
 * @param {Array} data
 */
const formatTreeData = (data) => {
  const stack = new Stack()
  const arr = []
  stack.push(data)
  data.parentIds = [Number(data.parentId)]
  while (stack.top) {
    const item = stack.pop()
    for (const i in item.children) {
      stack.push(item.children[i])
      item.children[i].parentIds = [
        ...item.parentIds,
        item.indexNo || Number(item.id),
      ]
    }
    const i = deepClone(item)
    if (i.children) delete i.children
    i.parentId = i.parentIds[i.parentIds.length - 1]
    arr.push(i)
  }
  return arr
}

/**
 * 防抖原理：一定时间内，只有最后一次操作，再过wait毫秒后才执行函数
 *
 * @param {Function} func 要执行的回调函数
 * @param {Number} wait 延时的时间
 * @param {Boolean} immediate 是否立即执行
 * @return null
 */
let timeout = null
const debounce = (func, wait = 500, immediate = true) => {
  // 清除定时器
  if (timeout !== null) clearTimeout(timeout)
  // 立即执行，此类情况一般用不到
  if (immediate) {
    const callNow = !timeout
    timeout = setTimeout(() => {
      timeout = null
    }, wait)
    if (callNow) typeof func === 'function' && func()
  } else {
    // 设置定时器，当最后一次操作后，timeout不会再被清除，所以在延时wait毫秒后执行func回调方法
    timeout = setTimeout(() => {
      typeof func === 'function' && func()
    }, wait)
  }
}

/**
 * 文件下载
 */
const downloadFile = (blobUrl) => {
  const eleLink = document.createElement('a')
  eleLink.download = ''
  eleLink.style.display = 'none'
  eleLink.href = blobUrl
  document.body.appendChild(eleLink)
  eleLink.click()
  document.body.removeChild(eleLink)
}

// 判断全屏
const checkFull = () => {
  // 判断浏览器是否处于全屏状态 （需要考虑兼容问题）
  // 火狐浏览器
  let isFull =
    document.mozFullScreen ||
    document.fullScreen ||
    // 谷歌浏览器及Webkit内核浏览器
    document.webkitIsFullScreen ||
    document.webkitRequestFullScreen ||
    document.mozRequestFullScreen ||
    document.msFullscreenEnabled
  if (isFull === undefined) {
    isFull = false
  }
  return isFull
}

/**
 * 验证是否存在true/false
 */
const vaildData = (val, dafult) => {
  if (typeof val === 'boolean') {
    return val
  }
  return !validatenull(val) ? val : dafult
}

// 验证空
const validatenull = (val) => {
  // 特殊判断
  if (val && parseInt(val) === 0) return false
  const list = ['$parent']
  if (typeof val === 'boolean') {
    return false
  }
  if (typeof val === 'number') {
    return false
  }
  if (val instanceof Array) {
    if (val.length === 0) return true
  } else if (val instanceof Object) {
    val = deepClone(val)
    list.forEach((ele) => {
      delete val[ele]
    })
    for (const o in val) {
      return false
    }
    return true
  } else {
    if (
      val === 'null' ||
      val == null ||
      val === 'undefined' ||
      val === undefined ||
      val === ''
    ) {
      return true
    }
    return false
  }
  return false
}

/**
 * 将字符串转为数组后重新组装成新字符串，过滤掉其中的空元素
 * @param tmpList 特殊字符数组
 * @param oldStr  原字符串
 * @param str   分隔符
 * @returns newStr
 */
const getNewStr = (tmpList, oldStr, str) => {
  // 1、将输入字符转为数组
  const strList = oldStr.split(str)
  // 2、过滤掉【空格】与【单独的特殊字符】后得到新的字符串
  let newInputVal = ''
  for (let m = 0; m < strList.length; m++) {
    const tmpValue = strList[m]
    if (tmpValue != '') {
      let status = true
      if (tmpValue.length == 1) {
        for (let k = 0; k < tmpList.length; k++) {
          const tmp = tmpList[k]
          if (tmpValue.indexOf(tmp) == 0) {
            status = false
            break
          }
        }
      }
      if (status == true) {
        if (newInputVal == '') {
          newInputVal = tmpValue
        } else {
          newInputVal = `${newInputVal}|${tmpValue}`
        }
      }
    }
  }
  return newInputVal
}

/**
 * 将字符串中的特殊字符全部替换成英文逗号
 * @param tmpList  特殊字符数组
 * @param newInputVal 字符串
 * @returns
 */
const replaceStr = (tmpList, newInputVal) => {
  for (let k = 0; k < tmpList.length; k++) {
    const tmp = tmpList[k]
    newInputVal = newInputVal.replaceAll(tmp, ',')
  }
  return newInputVal
}

/**
 * 过滤特殊字符
 */
// 1、定义需要替换的特殊符号
const tmpList = [
  ',',
  '，',
  ';',
  '；',
  '、',
  '|',
  '\n',
  '\t',
  '.',
  '。',
  '?',
  '？',
  ':',
  '：',
  '‘',
  '’',
  '~',
  '!',
  '@',
  '#',
  '$',
  '%',
  '^',
  '&',
  '*',
  '(',
  ')',
]

const specialStrFilter = (str) => {
  if (getObjType(str) != 'string') return ''
  // 2、将所有空格替换为“#”
  const inputVal = str.replace(/\s+/g, '#')
  // 3、将字符串转为数组后重新组装成新字符串，过滤掉其中的空元素
  let newInputVal = getNewStr(tmpList, inputVal, '#')
  // 4、将字符串中的特殊字符全部替换成英文逗号
  newInputVal = replaceStr(tmpList, newInputVal)
  // 5、将字符串转为数组后重新组装成新字符串，过滤掉其中的空元素
  newInputVal = getNewStr(tmpList, newInputVal, ',')
  // 6、将字符串中的特殊字符全部替换成英文逗号
  newInputVal = replaceStr(tmpList, newInputVal)
  return newInputVal
}

export default {
  Stack,
  deepClone,
  getObjType,
  formatTreeData,
  debounce,
  downloadFile,
  checkFull,
  alert,
  vaildData,
  validatenull,
  getNewStr,
  replaceStr,
  specialStrFilter,
}
